// ************************************************************* //
//                                                               //
//        Copyright (C) 2015 by Socionext Inc.                   //
//        All rights reserved.                                   //
//        Socionext Inc. Proprietary and Confidential.           //
//                                                               //
// ************************************************************* //
/*****************************************************************************/
/**
* @file em_general.c
* @brief eMMC general operation
* @note None
* @attention None
*
*/
/*****************************************************************************/

// include -------------------------------------
// #include "common.h"
// #include "timer.h"
#include "em_int_cmn.h"
#include "em_config.h"
#include "em_host.h"
#include "em_general.h"
#include "em_data.h"

extern unsigned long Timestamp_Get_us(void);

#undef FILEID
#define FILEID FILE_EM_GENERAL

// function -------------------------------------
static unsigned long em_CheckSlots(
	EM_HOST* pHost
);

static unsigned long em_HostInitialize(
	EM_HOST* pHost
);

/*****************************************************************************/
/**
 * @brief eMMC initialize
 * @param[in] pHost
 * @param[in] pConfig
 * @return EM_OK
 * @return EM_INVAL
 */
/*****************************************************************************/
unsigned long em_init(
	EM_HOST* pHost,
	EM_CONFIG *pConfig
){
	long iCnt = 0;
	long iCnt_2 = 0;
	unsigned long ul_Ret = EM_INVAL;
	unsigned long logAddr = 0;

	/**
	 * parameter check
	 */
	if (pHost == NULL) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}
	if (pConfig == NULL) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}
	if (pConfig->descLogAddress == NULL) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	/**
	 * Process start
	 */

	/*
	 * Set the host information
	 */
	// base address
	pHost->pBaseAddress = (EM_REGISTER_SFR*)(pConfig->ul_BaseAddress) ;
	// clear the host information
	pHost->uc_SlotCount = 0;
	pHost->uc_SpecVerNum = 0;

	// descriptor buffer
	logAddr = (unsigned long)pConfig->descLogAddress;

	for (iCnt = 0; iCnt < EM_CONFIG_SLOT_COUNT; iCnt++)
	{
		// clear the slot information
		pHost->sSlots[ iCnt ].pRegSRS = NULL;
		pHost->sSlots[ iCnt ].pDevice = NULL;
		pHost->sSlots[ iCnt ].pCurrentRequest = NULL;
		pHost->sSlots[ iCnt ].pParentHost = NULL;
		pHost->sSlots[ iCnt ].e_AccessMode = EM_ACCESS_MODE_INVALID;
		pHost->sSlots[ iCnt ].eDmaMode = EM_DMA_NONEDMA_MODE;
		pHost->sSlots[ iCnt ].ul_DMABufferBoundary = 0;
		pHost->sSlots[ iCnt ].uc_BusWidth = 0;
		pHost->sSlots[ iCnt ].uc_CardInserted = 0;
		pHost->sSlots[ iCnt ].uc_SlotNum = 0;
		pHost->sSlots[ iCnt ].uc_NeedAttach = 0;
		pHost->sSlots[ iCnt ].uc_errorRecover = 0;
		for (iCnt_2 = 0; iCnt_2 < (EXTCSD_SIZE / 4); iCnt_2++)
		{
			pHost->sSlots[ iCnt ].ul_AuxBuff[ iCnt_2 ] = 0;
			pHost->sSlots[ iCnt ].ul_ExtendsCSD[ iCnt_2 ] = 0;
		}
		for (iCnt_2 = 0; iCnt_2 < 4; iCnt_2++)
		{
			pHost->sSlots[ iCnt ].ul_CSDinfo[ iCnt_2 ] = 0;
		}
		//! ADMA descriptor area
		pHost->sSlots[ iCnt ].pDescriptorBuffer = (unsigned long*)(logAddr + iCnt * EM_CONFIG_MAX_DESCR_BUFF_SIZE);
	}

	//! host initialize
	ul_Ret = em_HostInitialize(
		pHost
	);
	if( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}
	
	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief eMMC boot start
 * @param[in] pHost
 * @param[in] ul_tryCount
 * @return EM_OK
 * @return EM_INVAL
 */
/*****************************************************************************/
unsigned long em_start(
	EM_HOST* pHost
){
	unsigned long ul_Ret = EM_INVAL;

	/**
	 * parameter check
	 */
	if( pHost == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	
	/**
	 * Process start
	 */

	ul_Ret = em_CheckSlots( pHost );
	if( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}
	
	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief eMMC host initialize
 * @param[in] pHost
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_HARDWARE_PROBLEM
 */
/*****************************************************************************/
static unsigned long em_HostInitialize(
	EM_HOST* pHost
){
	volatile unsigned long ul_crs63 = 0;
	unsigned long ul_Ret = 0;
	unsigned long ul_Cnt = 0;

	/**
	 * parameter check
	 */
	if( pHost == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	
	/**
	 * Process start
	 */
	//! CRS63
	ul_crs63 = EM_READ_REG( pHost->pBaseAddress->CRS.CRS63.word) ;
	//! Specification Version Number
	pHost->uc_SpecVerNum = (unsigned char)(CRS63_GET_SPEC_VER_NUM( ul_crs63 ));
	if (pHost->uc_SpecVerNum  < CRS63_HOST_SPEC_VER_3_00)
	{
		//! abnormal version
		DEBUG_TRACE_INFO_32( 0, pHost->uc_SpecVerNum );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;;
	}

	pHost->uc_SlotCount = 0;
	for (ul_Cnt = 0; ul_Cnt < EM_CONFIG_SLOT_COUNT; ul_Cnt++)
	{
		//! SRS position
		pHost->sSlots[ pHost->uc_SlotCount ].pRegSRS = &(pHost->pBaseAddress->sSRS_Slot[ ul_Cnt ].SRS) ;

		// slot number
		pHost->sSlots[ pHost->uc_SlotCount ].uc_SlotNum = (unsigned char)ul_Cnt;

		// slot count
		pHost->uc_SlotCount++;

	}
	
	// slot count check
	if( pHost->uc_SlotCount == 0 ) 
	{
		DEBUG_TRACE_INFO_32( 0, EM_ERR_HARDWARE_PROBLEM );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_HARDWARE_PROBLEM ;
	}

	/**
	 * initialize slot information
	 */
	for (ul_Cnt = 0; (ul_Cnt < pHost->uc_SlotCount) && (ul_Cnt < EM_CONFIG_SLOT_COUNT); ul_Cnt++)
	{
		ul_Ret = em_initializeSlot( &(pHost->sSlots[ ul_Cnt ]), pHost );
		if ( ul_Ret != EM_OK )
		{
			DEBUG_TRACE_INFO_32( 0, ul_Ret );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_INVAL;
		}
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief check slot
 * @param[in] pHost
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_CARD_IS_NOT_INSERTED
 **/
/*****************************************************************************/

static unsigned long em_CheckSlots(
	EM_HOST* pHost
){
	long iCnt = 0;
	unsigned long ul_Ret = 0;
	EM_SLOT* pSlot = NULL;

	/**
	 * parameter check
	 */
	if( pHost == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */

	for (iCnt = 0; (iCnt < pHost->uc_SlotCount) && (iCnt < EM_CONFIG_SLOT_COUNT); iCnt++)
	{
		// SRS9.CI check
		pSlot = &(pHost->sSlots[ iCnt ]);

		ul_Ret = em_waitForValue(
			&(pSlot->pRegSRS->SRS9.word),
			SRS9_CARD_INSERTED,
			1,
			EM_CONFIG_WAIT_VALUE_TIME);
		if ( ul_Ret != EM_OK )
		{
			// no card
			DEBUG_TRACE_INFO_32( 0, ul_Ret );
			DEBUG_TRACE_INFO_32( 4, pSlot->pRegSRS->SRS9.word );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_ERR_CARD_IS_NOT_INSERTED;
		}

		// device attach
		ul_Ret = em_deviceAttach(pSlot);
		if ( ul_Ret != EM_OK )
		{
			DEBUG_TRACE_INFO_32( 0, ul_Ret );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_INVAL;
		}
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief Interrupt Handler
 * @param[in] pHost
 * @param[in] ul_start_ms
 * @param[in] ul_timeout_value
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_TIMEOUT
 */
/*****************************************************************************/
unsigned long em_InterruptHandler(
	EM_HOST *pHost,
	unsigned long ul_start_ms,
	unsigned long ul_timeout_value
){
	unsigned long sSys_time = 0;
	EM_SLOT *pSlot = NULL;
	long iCnt = 0;
	unsigned long ul_srs12 = 0;
	unsigned long ul_Ret = EM_INVAL;
	
	/**
	 * parameter check
	 */
	if( pHost == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	/**
	 * Process start
	 */
	for (iCnt = 0; (iCnt < pHost->uc_SlotCount) && (iCnt < EM_CONFIG_SLOT_COUNT); iCnt++)
	{
		pSlot = &(pHost->sSlots[ iCnt ]);

		// get time
		sSys_time = (Timestamp_Get_us() / 1000);
		if( (sSys_time - ul_start_ms) > ul_timeout_value )
		{
			// over 20ms
			DEBUG_TRACE_INFO_32( 0, ul_start_ms );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_ERR_TIMEOUT;
		}
		// SRS12
		ul_srs12 = EM_READ_REG(pSlot->pRegSRS->SRS12.word);
		if( ul_srs12 == 0 )
		{
			break;
		}
		// clear the Normal status interrupts
		EM_WRITE_REG( pSlot->pRegSRS->SRS12.word, (ul_srs12 & SRS12_NORMAL_STAUS_MASK));
		
		// interrupt check
		ul_Ret = em_checkInterrupt(pSlot, ul_srs12);
		if( ul_Ret != EM_OK )
		{
			DEBUG_TRACE_INFO_32( 0, ul_Ret );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_INVAL;
		}
	}
	return EM_OK;
}


/*****************************************************************************/
/**
 * @brief eMMC end
 * @param[in] pHost
 * @return None
 */
/*****************************************************************************/
void em_end(
	EM_HOST* pHost
){
	long iCnt = 0;

	/**
	 * parameter check
	 */
	if( pHost == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return ;
	}
	/**
	 * Process start
	 */
	for (iCnt = 0; (iCnt < pHost->uc_SlotCount) && (iCnt < EM_CONFIG_SLOT_COUNT); iCnt++)
	{
		// CMD0
		em_recoverRegister(
			&(pHost->sSlots[ iCnt ])
		);
	}
}

